﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MSpecLauncher.Model;
using System.IO;

namespace MSpecLauncher.Services
{
    /// <summary>
    /// According with the configuration, executes the tests and builds a model with the results
    /// </summary>
    public class Launcher : ILauncher
    {
        private const string TXT_CONFIG_FILE_NOT_FOUND = "Config file not found: {0}";
        private const string TXT_RESULT_FILE_NOT_FOUND = "XML file with spec results not found: {0}";
        private const string TXT_NO_GIVEN_CONFIG_FILE = "There is no configuration. A config file is needed.";

        private IConfigBuilder configBuilder = null;
        private ICommandRunner specRunner = null;
        private IModelBuilder modelBuilder = null;
        private IFileChangesDetector changesDetector = null;

        private Config config = null;
        private SpecRun currentSpecRun = null;

        /// <summary>
        /// Event to notify that a new automatic run has completed
        /// </summary>
        public event Action AutomaticRunFinished;

        /// <summary>
        /// Gets html file name with specs results
        /// </summary>
        public string HTMLFileName
        {
            get
            {
                if (config == null) return null;
                return config.GenerateHTMLFileName();
            }
        }

        /// <summary>
        /// Gets file name with specs results
        /// </summary>
        public string ResultFileName
        {
            get
            {
                if (config == null) return null;
                return config.GenerateXMLFileName();
            }
        }

        /// <summary>
        /// Gets current spec run results
        /// </summary>
        public SpecRun SpecRun { get { return this.currentSpecRun; } }


        public Launcher(IConfigBuilder configBuilder, ICommandRunner specRunner, IModelBuilder modelBuilder,
            IFileChangesDetector changesDetector)
        {
            this.configBuilder = configBuilder;
            this.specRunner = specRunner;
            this.modelBuilder = modelBuilder;
            this.changesDetector = changesDetector;
            this.changesDetector.FileChanged += new Action<string>(changesDetector_FileChanged);
        }

        /// <summary>
        /// Load the given config file.
        /// Exceptions are propagated.
        /// </summary>
        public void LoadConfigFile(string configFile)
        {
            if (!string.IsNullOrEmpty(configFile))
            {
                if (File.Exists(configFile))
                {
                    // Build configuration model
                    this.config = this.configBuilder.Build(File.ReadAllText(configFile));

                    // Configure autodetection if enabled
                    if (this.config != null && this.config.AutodetectChanges)
                    {
                        // Clear previous auodetection files
                        this.changesDetector.ClearWatches();

                        // Add new assemblies to monitor for changes
                        foreach (string assembly in this.config.Assemblies)
                        {
                            this.changesDetector.AddFileToWatch(assembly);
                        }
                    }
                }
                else
                {
                    throw new ApplicationException(string.Format(TXT_CONFIG_FILE_NOT_FOUND, configFile));
                }
            }
        }

        /// <summary>
        /// Run the tests and update the model with the results.
        /// Exceptions are propagated.
        /// </summary>
        public void Run()
        {
            if (this.config == null)
            {
                throw new ApplicationException(TXT_NO_GIVEN_CONFIG_FILE);
            }
            else
            {
                // Generate output file names
                string xmlFileName = this.config.GenerateXMLFileName();
                string htmlFileName = this.config.GenerateHTMLFileName();

                string cmdArgs;
                string cmd = this.config.GenerateCmd(xmlFileName, htmlFileName, out cmdArgs);

                // Run tests
                specRunner.RunSpecs(cmd, cmdArgs, this.config.ShowCommandWindow);

                // Load results
                this.LoadResultFile(xmlFileName);
            }
        }

        /// <summary>
        /// Load the given run result file and update the model with its content.
        /// Exceptions are propagated.
        /// </summary>
        public void LoadResultFile(string xmlFileName)
        {
            // Cargamos los resultados
            if (File.Exists(xmlFileName))
            {
                // Build a model from the xml file
                string result = File.ReadAllText(xmlFileName);
                this.currentSpecRun = modelBuilder.Build(result);
            }
            else
            {
                throw new ApplicationException(string.Format(TXT_RESULT_FILE_NOT_FOUND, xmlFileName));
            }
        }

        /// <summary>
        /// Event handler for changes in assemblies
        /// </summary>
        private void changesDetector_FileChanged(string obj)
        {
            // One or more assembly files have changed

            // Run the specs and update the model
            this.Run();

            // Notify changes
            if (this.AutomaticRunFinished != null) this.AutomaticRunFinished();
        }

        

    }
}
